﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Transformer.iHunter.Plugins.BBS
{
    /// <summary>
    /// The terminal to display contents of BBS
    /// </summary>
    public class Terminal
    {
        #region Public Properties
        /// <summary>
        /// Row number
        /// </summary>
        public int RowNum
        {
            get { return rowNum; }
            private set
            {
                rowNum = value;
                termSize = rowNum * colNum;
            }
        }

        /// <summary>
        /// Column number
        /// </summary>
        public int ColNum
        {
            get { return colNum; }
            set
            {
                colNum = value;
                termSize = rowNum * colNum;
            }
        }

        /// <summary>
        /// Number of grids in the terminal, which is RowNum*ColNum
        /// </summary>
        public int GridNum
        {
            get { return termSize; }
        }

        /// <summary>
        /// Current cursor
        /// </summary>
        public Cursor CurrentCursor
        {
            get;
            private set;
        }

        /// <summary>
        /// Text contents
        /// </summary>
        public char[] Contents
        {
            get;
            private set;
        }

        /// <summary>
        /// Fore colors
        /// </summary>
        public Color[] ForeColors
        {
            get;
            private set;
        }

        /// <summary>
        /// Back colors
        /// </summary>
        public Color[] BackColors
        {
            get;
            private set;
        }

        public bool[] Updated
        {
            get;
            private set;
        }

        #endregion

        #region Fields
        private Color curForeColor;
        private Color curBackColor;
        private bool citeMode;
        private bool lightColorMode;

        private Regex twoParam_re = new Regex(@"(\d+);(\d+)");
        private string lastRemain = "";
        private bool IsLastByteRemain = false;
        private byte lastByteRemain = (byte)0;

        private int rowNum, colNum, termSize;
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor
        /// </summary>        
        public Terminal()
        {
            this.SetSize(24, 80);
        }
        #endregion
        
        #region Public Methods
        /// <summary>
        /// Reset the terminal's content to the initial state
        /// </summary>
        public void Reset()
        {
            for (int i = 0; i < GridNum; i++ )
            {
                Contents[i] = Character.NUL;
                ForeColors[i] = Color.DefaultForeColor;
                BackColors[i] = Color.DefaultBackColor;
            }
            
            CurrentCursor.MoveTo(0,0);

            curForeColor = Color.DefaultForeColor;
            curBackColor = Color.DefaultBackColor;

            citeMode = false;
            lightColorMode = false;
        }

        public void ClearUpdateFlag()
        {
            for (int i = 0; i < this.Updated.Length; i++)
                Updated[i] = false;
        }

        /// <summary>
        /// Get the whole plain text in the terminal(without color)
        /// </summary>
        /// <returns>An array of string, each string is a line in the terminal</returns>
        public string[] GetLines()
        {
            return GetLines(0, this.RowNum - 1);
        }

        public string GetLine(int index)
        {
            if (index < 0 || index >= this.RowNum)
                return "";

            return GetLines(index, index)[0];
        }

        public string ToText(int startLine, int endLine)
        {
            string[] lines = GetLines(startLine, endLine);

            string str = "";

            foreach (string line in lines)
            {
                str += (line + "\n");
            }

            return str;
        }

        public string ToText()
        {
            return ToText(0, this.RowNum - 1);
        }

        /// <summary>
        /// Get the plain text of specified lines in the terminal
        /// </summary>
        /// <param name="startLine">The index of the start line (included)</param>
        /// <param name="endLine">The index of the end line (included)</param>
        /// <returns>An array of string, each string is a line in the terminal</returns>
        public string[] GetLines(int startLine, int endLine)
        {
            if (startLine < 0)
                startLine = 0;

            if (endLine >= this.RowNum)
                endLine = this.RowNum - 1;

            if (startLine > endLine)
                return new string[0];

            List<string> lines = new List<string>();

            for (int i = startLine; i <= endLine; i++)
            {
                string line = "";

                for (int j = 0; j < ColNum; j++)
                {
                    int ind = this.Point2Index(i,j);

                    char ch = this.Contents[ind];

                    if (Character.NeedTwoByte(ch))
                    {
                        line += ch;
                        j++;
                    }
                    else
                    {
                        switch (ch)
                        {
                            case '\r':
                            case Character.NUL:
                                line += " ";
                                break;

                            default:
                                line += ch;
                                break;
                        }
                    }
                }

                lines.Add(line);
            }

            return lines.ToArray();
        }
        
        /// <summary>
        /// Convert the content of the terminal to Html
        /// </summary>
        /// <returns>The Html codes</returns>
        public string ToHtml()
        {
            return ToHtml(0, this.RowNum - 1);
        }

        /// <summary>
        /// Convert the content of specified lines to Html
        /// </summary>
        /// <param name="startLine">The index of the start line (included)</param>
        /// <param name="endLine">The index of the end line (included)</param>
        /// <returns>The Html codes</returns>
        public string ToHtml(int startLine, int endLine)
        {
            if (startLine < 0)
                startLine = 0;

            if (endLine >= this.RowNum)
                endLine = this.RowNum - 1;

            if (startLine > endLine)
                return "";

            string html = "<meta content=\"charset=UTF-8\"><style type=\"text/css\">\n.songti\n{\nfont-family: 宋体;\n}\n.monospace\n{\nfont-family: monospace;\n}\n.nowrap{white-space:nowrap;}\n</style>\n<body bgcolor=\"#000000\"><div class=\"nowrap\">";

            int startIndex = this.Point2Index(startLine, 0);

            Color fore = this.ForeColors[startIndex];
            Color back = this.BackColors[startIndex];

            html += string.Format("<span class=\"songti\", style = \"background-color: {0}\"><font color=\"{1}\">", 
                GetHtmlColorCode(back), GetHtmlColorCode(fore));

            for (int i = startLine; i <= endLine; i++)
            {
                for (int j = 0; j < ColNum; j++)
                {
                    int index = this.Point2Index(i, j);

                    if (this.Contents[index] == Character.NUL)
                    {
                        if (fore != Color.DefaultForeColor)
                        {
                            fore = Color.DefaultForeColor;
                            html += string.Format("</font><font color=\"{0}\">", GetHtmlColorCode(fore));
                        }

                        if (back != Color.DefaultBackColor)
                        {
                            back = Color.DefaultBackColor;
                            html += string.Format("</span><span class=\"songti\", style = \"background-color: {0}\">", GetHtmlColorCode(back));
                        }
                                           
                       
                        html += Character.HtmlSpace;
                    }
                    else
                    {
                        if (fore != this.ForeColors[index])
                        {
                            fore = this.ForeColors[index];
                            html += string.Format("</font><font color=\"{0}\">", GetHtmlColorCode(fore));
                        }

                        if (back != this.BackColors[index])
                        {
                            back = this.BackColors[index];
                            html += string.Format("</span><span class=\"songti\", style = \"background-color: {0}\">", GetHtmlColorCode(back));
                        }

                        char ch = this.Contents[index];
                        if (Character.NeedTwoByte(ch))
                        {
                            html += ch;
                            j++;
                        }
                        else
                        {
                            switch (ch)
                            {
                                case (char)9:
                                case ' ':
                                    html += Character.HtmlSpace;
                                    break;

                                case '<':
                                    html += Character.HtmlLT;
                                    break;

                                case '>':
                                    html += Character.HtmlGT;
                                    break;

                                case '\"':
                                    html += Character.HtmlQuot;
                                    break;

                                case '&':
                                    html += Character.HtmlAnd;
                                    break;

                                default:
                                    html += ch;
                                    break;
                            }
                        }

                        
                    }
                }

                /*if (this.Updated[i])
                {
                    html += "(updated)";
                }*/
                    
                if (i <= endLine)
                html += "<br>";
            }

            html += "</font></span></div></body>";

            return html;
        }        

        /// <summary>
        /// Write the data received from the server to the terminal
        /// </summary>
        /// <param name="data">Data received</param>
        public void Write(byte[] data)
        {
             
            if (IsLastByteRemain)
            {
                byte[] bytes = new byte[data.Length + 1];
                bytes[0] = lastByteRemain;
                for (int i = 0; i < data.Length; i++)
                    bytes[i + 1] = data[i];

                data = bytes;
            }

            int len = data.Length;
            if (LastCharIsTruncated(data))
            {
                len--;
                IsLastByteRemain = true;
                lastByteRemain = data[data.Length-1];
            }
            else
                IsLastByteRemain = false;

            string text = System.Text.Encoding.GetEncoding("gb2312").GetString(data, 0, len);

            //byte[] bytes = System.Text.Encoding.GetEncoding("gb2312").GetBytes(text);
            if (lastRemain != "")
            {
                text = lastRemain + text;
                lastRemain = "";
            }

            /*System.IO.StreamWriter sw = new System.IO.StreamWriter(@"D:\My files\temp\log.txt", true);
            sw.Write(text);
            sw.Close();*/

            for (int i = 0; i < text.Length; i++)
            {
                //char ch = System.Convert.ToChar(bytes[i]);
                char ch = text[i];

                //int index = Cursor.Index;

                switch (ch)
                {
                    case Character.ESC:
                        {
                            if (i + 1 >= text.Length)
                            {
                                lastRemain += Character.ESC;
                                break;
                            }
                            else if (text[i+1] != '[')
                            {
                                Write('*');
                                break;
                            }
                            else
                            {
                                int j = i + 2;
                                while (j < text.Length && text[j] != Character.CR
                                    && (!char.IsLetter(text[j])))
                                    j++;

                                if (j >= text.Length)
                                {
                                    lastRemain += text.Substring(i);
                                    i = j;
                                    break;
                                }
                                else if (text[j] == Character.CR)
                                {
                                    Write('*');
                                    break;
                                }
                                else
                                {
                                    char cmdChar = text[j];
                                    string param = "";
                                    if (j > i + 2)
                                        param = text.Substring(i + 2, j - i - 2);

                                    ParseCmd(cmdChar, param);

                                    i = j;
                                }
                            }
                        }

                        break;
                    case ':':
                        {
                            if (CurrentCursor.Y == 0 && i+1<text.Length &&
                                text[i+1] == ' ')
                            {
                                curForeColor = Color.DarkCyan;
                                citeMode = true;
                            }

                            Write(ch);
                        }
                        break;
                    case '\r':
                        /*if (i > 0 && text[i - 1] == 'B')
                        {
                            ch = '\r';
                        }*/

                        //Write(ch);

                        CurrentCursor.MoveTo(CurrentCursor.X, 0);
                        break;
                    case '\n':
                        if (citeMode)
                        {
                            curForeColor = Color.DefaultForeColor;
                            citeMode = false;
                        }

                        this.Updated[CurrentCursor.X] = true;
                        CurrentCursor.MoveToNextLine();

                        if (CurrentCursor.NeedNewLine)
                            this.AppendNewLine(); 

                        break;
                    default:
                        {
                            Write(ch);
                        }
                        break;
                }

                
            }

            
        }

        /// <summary>
        /// Convert two-dimension point to index in one-dimension array
        /// </summary>
        /// <param name="x">x coordinate</param>
        /// <param name="y">y coordinate</param>
        /// <returns>Index in one-dimension array</returns>
        public int Point2Index(int x, int y)
        {
            return x * colNum + y;
        }
        #endregion

        #region Private Methods
        private void SetSize(int rNum, int cNum)
        {
            this.RowNum = rNum;
            this.ColNum = cNum;

            this.CurrentCursor = new Cursor(RowNum, ColNum);
            this.Contents = new char[GridNum];
            this.ForeColors = new Color[GridNum];
            this.BackColors = new Color[GridNum];
            this.Updated = new bool[RowNum];

            Reset();
            ClearUpdateFlag();
        }

        private static string GetHtmlColorCode(Color color)
        {
            switch (color)
            {
                case Color.Black:
                //case Color.DefaultForeColor:
                case Color.DefaultBackColor:
                    return "000000";
                    
                case Color.DarkRed:
                    return "660000";
                    
                case Color.DarkGreen:
                    return "336600";
                    
                case Color.DarkYellow:
                    return "cccc00";
                    
                case Color.DarkBlue:
                    return "330099";
                    
                case Color.DarkPink:
                    return "660099";
                    
                case Color.DarkCyan:
                    return "339999";
                    
                //case Color.DefaultBackColor:
                case Color.DefaultForeColor:
                case Color.White:
                    return "ffffff";
                    
                case Color.Grey:
                    return "999999";
                    
                case Color.Red:
                    return "ff0000";
                    
                case Color.Green:
                    return "00ff00";
                    
                case Color.Yellow:
                    return "ffff00";
                   
                case Color.Blue:
                    return "0000ff";
                    
                case Color.Pink:
                    return "ff66ff";
                    
                case Color.Cyan:
                    return "33ffff";
                    
                case Color.ShiningWhite:
                    return "ffffcc";                   
            }

            return "000000";
        }

        private void ParseCmd(char ctrlChar, string param)
        {
            switch(ctrlChar)
            {
                case 'A':
                    {
                        int k = 1;
                        int temp=1;
                        if (int.TryParse(param, out temp))
                            k = temp;

                        this.Updated[CurrentCursor.X] = true;

                        for (int times = 0; times < k; times++)
                            CurrentCursor.MoveUp();
                    }
                    break;
                case 'B':
                    {
                        int k = 1;
                        int temp = 1;
                        if (int.TryParse(param, out temp))
                            k = temp;

                        this.Updated[CurrentCursor.X] = true;

                        for (int times = 0; times < k; times++)
                            CurrentCursor.MoveDown();
                    }
                    break;
                case 'C':
                    {
                        int k = 1;
                        int temp = 1;
                        if (int.TryParse(param, out temp))
                            k = temp;

                        //this.Updated[CurrentCursor.X] = true;

                        for (int times = 0; times < k; times++)
                            CurrentCursor.MoveRight();
                    }
                    break;
                case 'D':
                    {
                        int k = 1;
                        int temp = 1;
                        if (int.TryParse(param, out temp))
                            k = temp;

                        this.Updated[CurrentCursor.X] = true;

                        for (int times = 0; times < k; times++)
                            CurrentCursor.MoveLeft();
                    }
                    break;
                case 'm':
                    if (param == "")
                    {
                        SetColor(0);
                    }
                    else
                    {
                        string[] ps = param.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                        foreach (string p in ps)
                        {
                            int k = 0;
                            if (int.TryParse(p,out k))
                            {
                                SetColor(k);
                            }
                        }
                    }

                    break;
                case 'K':
                    ClearLine(CurrentCursor.X, CurrentCursor.Y);

                    break;
                case 'J':
                    Reset();
                    break;
                case 'H':
                    {
                        Match m = twoParam_re.Match(param);                        
                        if (m.Success && m.Length == param.Length)
                        {
                            int x = int.Parse(m.Groups[1].Value);
                            int y = int.Parse(m.Groups[2].Value);
                                           

                            if (x <= 0 || y <= 0 || x > RowNum || y > ColNum)
                                break;

                            this.Updated[CurrentCursor.X] = true;
                            CurrentCursor.MoveTo(x-1, y-1);
                        }
                        else
                        {
                            Reset();
                        }
                    }

                    break;
                default:
                    // just abandon it
                    Console.WriteLine("Unknown cmd: "+ctrlChar);
                    break;
            }
        }

        private void SetColor(int param)
        {
            switch(param)
            {
                case 0:
                    this.lightColorMode = false;
                    this.curBackColor = Color.DefaultBackColor;
                    this.curForeColor = Color.DefaultForeColor;

                    break;

                case 1:
                    this.lightColorMode = true;
                    break;

                case 30:
                    if (this.lightColorMode)
                        this.curForeColor = Color.Grey;
                    else
                        this.curForeColor = Color.Black;
                    break;

                case 31:
                    if (this.lightColorMode)
                        this.curForeColor = Color.Red;
                    else
                        this.curForeColor = Color.DarkRed;
                    break;

                case 32:
                    if (this.lightColorMode)
                        this.curForeColor = Color.Green;
                    else
                        this.curForeColor = Color.DarkGreen;
                    break;

                case 33:
                    if (this.lightColorMode)
                        this.curForeColor = Color.Yellow;
                    else
                        this.curForeColor = Color.DarkYellow;
                    break;

                case 34:
                    if (this.lightColorMode)
                        this.curForeColor = Color.Blue;
                    else
                        this.curForeColor = Color.DarkBlue;
                    break;

                case 35:
                    if (this.lightColorMode)
                        this.curForeColor = Color.Pink;
                    else
                        this.curForeColor = Color.DarkPink;
                    break;

                case 36:
                    if (this.lightColorMode)
                        this.curForeColor = Color.Cyan;
                    else
                        this.curForeColor = Color.DarkCyan;
                    break;

                case 37:
                    if (this.lightColorMode)
                        this.curForeColor = Color.ShiningWhite;
                    else
                        this.curForeColor = Color.White;
                    break;

                case 40:
                    this.curBackColor = Color.Black;
                    break;

                case 41:
                    this.curBackColor = Color.DarkRed;
                    break;

                case 42:
                    this.curBackColor = Color.DarkGreen;
                    break;

                case 43:
                    this.curBackColor = Color.DarkYellow;
                    break;

                case 44:
                    this.curBackColor = Color.DarkBlue;
                    break;

                case 45:
                    this.curBackColor = Color.DarkPink;
                    break;

                case 46:
                    this.curBackColor = Color.DarkCyan;
                    break;
                    
                case 47:
                    this.curBackColor = Color.Grey;
                    break;
            }
        }

        private void Write(char ch)
        {
            if (CurrentCursor.ExceedLineLength)
                return;
                                  

            if (Character.NeedTwoByte(ch))
            {
                if (CurrentCursor.Y == this.ColNum - 1)
                {
                    Write(Character.NUL);
                    return;
                }

                int index = CurrentCursor.Index;
                Contents[index] = ch;
                ForeColors[index] = curForeColor;
                BackColors[index] = curBackColor;
                                
                Contents[index+1] = Character.NUL;
                CurrentCursor.MoveTo(CurrentCursor.X, CurrentCursor.Y+2);
            }
            else
            {
                    int index = CurrentCursor.Index;

                Contents[index] = ch;
                ForeColors[index] = curForeColor;
                BackColors[index] = curBackColor;

                CurrentCursor.MoveToNext();
            }
        }

        private void AppendNewLine()
        {
            int start = 0;           

            /*if (!afterEnter)
            {
                start = RowNum - 1;
                for (; start >= 0; start--)
                {
                    bool allblank = true;

                    for (int j = 0; j < ColNum; j++)
                    {
                        int ind = Cursor.Point2Index(start, j);
                        if (this.Contents[ind] != Character.NUL &&
                            this.Contents[ind] != ' ' && this.Contents[ind] != '\r')
                        {
                            allblank = false;
                            break;
                        }
                    }

                    if (allblank)
                        break;
                }

                if (start < 0)
                    start = 0;
            }

            start = 0;*/

            for (int i = start; i < RowNum - 1; i++)
            {
                for (int j = 0; j < ColNum; j++)
                {
                    int ind = this.Point2Index(i, j);
                    int below = this.Point2Index(i + 1, j);

                    this.Contents[ind] = this.Contents[below];
                    this.ForeColors[ind] = this.ForeColors[below];
                    this.BackColors[ind] = this.BackColors[below];
                }

                this.Updated[i] = this.Updated[i + 1];
            }

            for (int j = 0; j < ColNum; j++)
            {
                int ind = this.Point2Index(RowNum - 1, j);

                this.Contents[ind] = Character.NUL;
                this.ForeColors[ind] = Color.DefaultForeColor;
                this.BackColors[ind] = Color.DefaultBackColor;
            }

            this.Updated[RowNum - 1] = false;

            CurrentCursor.MoveTo(RowNum - 1, 0);
        }

        // *[K
        private void ClearLine(int x, int startY)
        {
            int ind = this.Point2Index(x, startY);
            int end = this.Point2Index(x, ColNum - 1);

            while (ind <= end)
            {
                Contents[ind] = ' ';
                ForeColors[ind] = curForeColor;
                BackColors[ind] = curBackColor;

                ind++;
            }

            this.Updated[x] = true;
        }

        private bool LastCharIsTruncated(byte[] bytes)
        {
            int i = 0;
            while (i < bytes.Length)
            {
                if (bytes[i] >= (byte)128 || bytes[i] < 0)
                    i += 2;
                else
                    i++;
            }

            return (i != bytes.Length);
        }

        #endregion

        #region Public Classes
        /// <summary>
        /// Color
        /// </summary>
        public enum Color
        {
            DefaultForeColor,
            DefaultBackColor,
            Black,      // *[30m & *[40m
            DarkRed,    // *[31m & *[41m
            DarkGreen,  // *[32m & *[42m
            DarkYellow, // *[33m & *[43m
            DarkBlue,   // *[34m & *[44m
            DarkPink,   // *[35m & *[45m
            DarkCyan,   // *[36m & *[46m & ": " at line start 
            White,      // *[37m
            Grey,       // *[1;30m & *[47m
            Red,        // *[1;31m
            Green,      // *[1;32m
            Yellow,     // *[1;33m
            Blue,       // *[1;34m
            Pink,       // *[1;35m
            Cyan,       // *[1;36m
            ShiningWhite// *[1;37m
        }

        /// <summary>
        /// The cursor in the terminal
        /// </summary>
        public class Cursor : IComparable<Cursor>
        {
            /// <summary>
            /// X coordinate
            /// </summary>
            public int X
            {
                get { return x; }
                private set
                {
                    x = value;
                    if (IsValid(x, y))
                        index = x * colNum + y;
                }
            }

            /// <summary>
            /// Y coordinate
            /// </summary>
            public int Y
            {
                get { return y; }
                private set
                {
                    y = value;
                    if (IsValid(x, y))
                        index = x * colNum + y;
                }
            }

            /// <summary>
            /// Index of the cursor in one-dimension array
            /// </summary>
            public int Index
            {
                get { return index; }
            }            

            /// <summary>
            /// Whether the cursor is in the line below the bottom of the terminal, 
            /// in which case the terminal has to append a new line and discard the top line
            /// </summary>
            public bool NeedNewLine
            {
                get { return (x == rowNum); }
            }

            /// <summary>
            /// Whether the cursor's Y coordinate exceeds the line length of the terminal,
            /// in which case the character in current position will not be displayed in the terminal
            /// </summary>
            public bool ExceedLineLength
            {
                get { return (y >= colNum); }
            }            

            private int x, y, index;
            private int rowNum, colNum, termSize;

            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="rNum">Row number</param>
            /// <param name="cNum">Column number</param>
            public Cursor(int rNum, int cNum)
            {
                rowNum = rNum;
                colNum = cNum;
                termSize = rowNum * colNum;

                X = Y = 0;
            }

            /// <summary>
            /// Tells whether the point is the terminal's valid area
            /// </summary>
            /// <param name="_x">x coordinate</param>
            /// <param name="_y">y coordinate</param>
            /// <returns>whether the point is the terminal's valid area</returns>
            public bool IsValid(int _x, int _y)
            {
                if (_x >= 0 && _x < rowNum &&
                    _y >= 0 && _y < colNum)
                    return true;

                return false;
            }

            /// <summary>
            /// Move to the specified point
            /// </summary>
            /// <param name="_x">x coordinate</param>
            /// <param name="_y">y coordinate</param>
            public void MoveTo(int _x, int _y)
            {

                this.X = _x;
                this.Y = _y;
            }

            /// <summary>
            /// Move up the cursor
            /// </summary>
            public void MoveUp()
            {
                if (this.X > 0)
                    MoveTo(this.X - 1, this.Y);
            }

            /// <summary>
            /// Move down the cursor
            /// </summary>
            public void MoveDown()
            {
                if (this.X < rowNum - 1)
                    MoveTo(this.X + 1, this.Y);
            }

            /// <summary>
            /// Move the cursor to left
            /// </summary>
            public void MoveLeft()
            {
                if (this.Y > 0)
                    MoveTo(this.X, this.Y - 1);
            }

            /// <summary>
            /// Move the cursor to right
            /// </summary>
            public void MoveRight()
            {
                if (this.Y < colNum - 1)
                    MoveTo(this.X, this.Y + 1);
            }

            /// <summary>
            /// Move the cursor to next place
            /// </summary>
            public void MoveToNext()
            {
                MoveTo(this.X, this.Y + 1);
            }

            /// <summary>
            /// Move the cursor to the start of next line
            /// </summary>
            public void MoveToNextLine()
            {
                MoveTo(this.X + 1, 0);
            }


            #region IComparable<Grid> Members
            /// <summary>
            /// Compare the cursor to another
            /// </summary>
            /// <param name="other">another cursor</param>
            /// <returns>the comparison result</returns>
            public int CompareTo(Cursor other)
            {
                if (this.X > other.X)
                    return 1;
                else if (this.X < other.X)
                    return -1;
                else
                    return (this.Y.CompareTo(other.Y));
            }

            #endregion
        }
        #endregion
    }
    
    
}
